#pragma once
#include "vertex_description.h"
#include "vector3.h"

namespace krryn{
namespace scene{
#pragma pack(push)
#pragma pack(1)
	class vertex_no_position{
	public:
		static const bool m_Available = false;
		static const size_t m_Stride = 0;
		static const size_t m_Size = 0;
	};

	class vertex_no_color{
	public:
		static const bool m_Available = false;
		static const size_t m_Stride = 0;
		static const size_t m_Size = 0;
	};

	class vertex_no_normal{
	public:
		static const bool m_Available = false;
		static const size_t m_Stride = 0;
		static const size_t m_Size = 0;
	};

	template
	<
		typename _Parent,
		typename _Position = math::vector3f, 
		size_t _Size = 3
	>
	class vertex_position{
	public:
		static const bool m_Available = true;
		static const size_t m_Stride = sizeof(_Position);
		static const size_t m_Size = _Size;

		_Position &get_position(){
			return m_Position;
		}

		const _Position &get_position() const{
			return m_Position;
		}

		_Parent &position(const _Position &a_Position){
			m_Position = a_Position;
			return static_cast<_Parent&>(*this);
		}
		const char *get_data() const { return (const char*)&m_Position; }
	private:
		_Position m_Position;
	};

	template
	<
		typename _Parent,
		typename _Color = math::color3, 
		size_t _Size = 3
	>
	class vertex_color{
	public:
		static const bool m_Available = true;
		static const size_t m_Stride = sizeof(_Color);
		static const size_t m_Size = _Size;

		_Color &get_color(){
			return m_Color;
		}

		const _Color &get_color() const{
			return m_Color;
		}

		_Parent &color(const _Color &a_Color){
			m_Color = a_Color;
			return static_cast<_Parent&>(*this);
		}

		const char *get_data() const { return (const char*)&m_Color; }
	private:
		_Color m_Color;
	};

	template
	<
		typename _Parent,
		typename _Normal = math::normal3f
	>
	class vertex_normal{
	public:
		static const bool m_Available = true;
		static const size_t m_Stride = sizeof(_Normal);
		static const size_t m_Size = 3;

		_Normal &get_normal(){
			return m_Normal;
		}

		const _Normal &get_normal() const{
			return m_Normal;
		}

		_Parent &normal(const _Normal &a_Normal){
			m_Normal = a_Normal;
			return static_cast<_Parent&>(*this);
		}

		const char *get_data() const { return (const char*)&m_Normal; }
	private:
		_Normal m_Normal;
	};

	template<typename _Type, bool _Available = _Type::m_Available>
	class vertex_copy{
	public:
		static size_t copy(const _Type &a_Type, char *a_Start, char *a_End){
			const char *l_Data = a_Type.get_data();

			if(a_Start + _Type::m_Stride > a_End){
				throw scene_exception("Out of bounds");
			}

			memcpy(a_Start, l_Data, _Type::m_Stride);

			return _Type::m_Stride;
		}
	};

	template<typename _Type>
	class vertex_copy<_Type, false>{
	public:
		static size_t copy(const _Type &a_Vertex, char *a_Start, char *a_End){
			return 0;
		}
	};

	template
	<
		typename _Position,
		typename _Color,
		typename _Normal,
		typename _UV
	>
	class basic_vertex
		: public _Position, 
		  public _Color, 
		  public _Normal, 
		  public _UV
	{
	public:
		typedef vertex_description
			<
				_Position, 
				_Color, 
				_Normal, 
				_UV,
				basic_vertex
			> description_type;

		void copy_into(char *a_Start, char *a_End) const{
			// validate: ptrdiff_t a_Diff = a_End - a_Start;

			a_Start += vertex_copy<_Position>::copy(*this, a_Start, a_End);
			a_Start += vertex_copy<_Color>::copy(*this, a_Start, a_End);
			a_Start += vertex_copy<_Normal>::copy(*this, a_Start, a_End);
			a_Start += vertex_copy<_UV>::copy(*this, a_Start, a_End);
		}
	};
#pragma pack(pop)
}
}